﻿using PIDHelper;
using STLINKFlash;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using static STLINKFlash.STLINKV2;

namespace STLinkProxy
{
    public partial class Form1 : Form
    {
        UdpClient udp;
        Action<byte[]>[] action;
        int[] callCount;
        IPEndPoint user = new IPEndPoint(IPAddress.Any, 52);
        string[] defstr=new string[256];
        bool sortupdate = false;
        STLINKV2 stlink = new STLINKV2();
        bool isopen = false;
        StringBuilder sb = new StringBuilder();
        byte cmd1, cmd2;
        byte cmd3;

        COMHelper COM;
        class callCountDef
        {
            public int Count=0;
            public int id = 0;
        }
        List<callCountDef> sort=new List<callCountDef>();
        List<callCountDef> callCountDefs=new List<callCountDef>();
        void InitOpt(int max)
        {
            for (int i = 0; i < max; i++)
            {
                var l = new Label();
                l.Location = new Point(info.Left, info.Top + i * 12);
                l.Size = new Size(mainlist.Width-25, info.Height);
                l.Text = ""+i;
                lablist.Add(l);
                mainlist.Controls.Add(l);
            }
            for (int i = 0; i < defstr.Length; i++) defstr[i] = "";
            defstr[0x01] = "GETSTATUS";
            defstr[0x02] = "FORCEDEBUG";
            defstr[0x03] = "APIV1_RESETSYS";
            defstr[0x04] = "APIV1_READALLREGS";
            defstr[0x05] = "APIV1_READREG";
            defstr[0x06] = "APIV1_WRITEREG";
            defstr[0x07] = "READMEM_32BIT";
            defstr[0x08] = "WRITEMEM_32BIT";
            defstr[0x09] = "RUNCORE";
            defstr[0x0a] = "STEPCORE";
            defstr[0x0b] = "APIV1_SETFP";
            defstr[0x0c] = "READMEM_8BIT";
            defstr[0x0d] = "WRITEMEM_8BIT";
            defstr[0x0e] = "APIV1_CLEARFP";
            defstr[0x0f] = "APIV1_WRITEDEBUGREG";
            defstr[0x10] = "APIV1_SETWATCHPOINT";
            defstr[0x00] = "ENTER_JTAG_RESET";
            defstr[0xa3] = "ENTER_SWD_NO_RESET";
            defstr[0xa4] = "ENTER_JTAG_NO_RESET";
            defstr[0x20] = "APIV1_ENTER";
            defstr[0x21] = "EXIT";
            defstr[0x22] = "READCOREID";
            defstr[0x30] = "APIV2_ENTER";
            defstr[0x31] = "APIV2_READ_IDCODES";
            defstr[0x32] = "APIV2_RESETSYS";
            defstr[0x33] = "APIV2_READREG";
            defstr[0x34] = "APIV2_WRITEREG";
            defstr[0x35] = "APIV2_WRITEDEBUGREG";
            defstr[0x36] = "APIV2_READDEBUGREG";
            defstr[0x3A] = "APIV2_READALLREGS";
            defstr[0x3B] = "APIV2_GETLASTRWSTATUS";
            defstr[0x3C] = "APIV2_DRIVE_NRST";
            defstr[0x3E] = "APIV2_GETLASTRWSTATUS2";
            defstr[0x40] = "APIV2_START_TRACE_RX";
            defstr[0x41] = "APIV2_STOP_TRACE_RX";
            defstr[0x42] = "APIV2_GET_TRACE_NB";
            defstr[0x43] = "APIV2_SWD_SET_FREQ";
            defstr[0x44] = "APIV2_JTAG_SET_FREQ";
            defstr[0x45] = "APIV2_READ_DAP_REG";
            defstr[0x46] = "APIV2_WRITE_DAP_REG";
            defstr[0x47] = "APIV2_READMEM_16BIT";
            defstr[0x48] = "APIV2_WRITEMEM_16BIT";
            defstr[0x4B] = "APIV2_INIT_AP";
            defstr[0x4C] = "APIV2_CLOSE_AP_DBG";
            defstr[0x50] = "WRITEMEM_32BIT_NO_ADDR_IN";
            defstr[0x51] = "APIV2_RW_MISC_OUT";
            defstr[0x52] = "APIV2_RW_MISC_IN";
            defstr[0x54] = "READMEM_32BIT_NO_ADDR_INC";
            defstr[0x61] = "SET_COM_FREQ";
            defstr[0x62] = "GET_COM_FREQ";
            defstr[0xF1] = "GET_VERSION";
            defstr[0xF2] = "DEBUG_COMMAND";
            defstr[0xF3] = "DFU_COMMAND";
            defstr[0xF5] = "GET_CURRENT_MODE";
            defstr[0xF7] = "GET_TARGET_VOLTAGE";
            defstr[0xFB] = "GET_VERSION_APIV3";
            var h = stlink.getHandle();
            if (h.Count != 0)
                retryopen.Visible = !stlink.openSTLink(h[0]);
            else
                info.Text = "打开stlink失败";

        }

        List<Label> lablist = new List<Label>();

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            Environment.Exit(0);
        }
        public Form1()
        {
            InitializeComponent();
            COM = new COMHelper(panel3);
            action = new Action<byte[]>[256];
            callCount=new int[256];

            for (int i = 0; i < action.Length; i++)
            {
                var c = new callCountDef();
                c.id = i;
                callCountDefs.Add(c);
                sort.Add(c);
                action[i] = (b) => { };
            }
            InitOpt(80);

            int sendcount = 0;
            List<byte[]> bytescache = new List<byte[]>();
            int lastcmd = 0;
            udp = new UdpClient(51);
            udp.Client.IOControl(-1744830452, new byte[] { Convert.ToByte(false) }, null);
            new Thread(() => {
                int pid = 0xff;
                union u = new union();
                byte[] lastpacket=new byte[6];
                byte[] bytes = new byte[10240];
                while (udp != null)
                {
                    var data=udp.Receive(ref user);
                    if (data.Length < 5)
                    {
                        if (data[0] == 't' && data[1] == 'e')
                        {
                            //if (user.Address == IPAddress.Any)
                                udp.Send(new byte[] { 0xff, 0xff ,0xFF,0xff}, 4,user);
                        }
                        continue;
                    }
                    if (pid == data[1]) continue;
                    pid = data[0 + 1];
                    data[0 + 1] = (byte)((data[0 + 0] & 0x80) != 0 ? 1 : 0);
                    data[0 + 0] &= 0x7f;

                    if (data[4] == 0xf2)
                    {
                        sortupdate = true;
                        callCountDefs[data[5]].Count++;
                        action[data[5]](data);
                    }
                    if ((data[4] & 0xf0) == 0xf0)
                    {
                        cmd1 = data[4];
                        cmd2 = data[5];
                        u.b = data;
                        if (COM.COM.IsOpen)
                        {
                            switch (data[4])
                            {
                                case 0xf1:
                                    u.b = bytes;
                                    u.ui[0] = 0x0483c728;
                                    u.ui[1] = 0x7a563748;
                                    udp.Send(bytes, 6, user);
                                    break;
                                case 0xf2:
                                    //var b = new byte[] { (byte)sendcount++ };
                                    //COM.SendUartData(b);
                                    //data[0] = (byte)sendcount++;
                                    sb.AppendLine($">{defstr[data[5]]} 0x{(u.ui[1] >> 16) | (uint)u.us[4] << 16:X8} 0x{u.us[5]:X4} {(u.us[7] | (u.us[8] << 16)):X8}");
                                    if (data[5] == 0x08)
                                    {
                                        var ag = new byte[data.Length];
                                        for(int i=0;i<12;i++)
                                            ag[i] = data[i];
                                        var c = (4 + data[0]);
                                        var xc = (data[2] + (data[3]<<8));
                                        for (int i = 0; i < xc; i++)
                                            ag[i+12] = data[c+i];
                                        data = ag;
                                    }
                                    if (lastcmd == 0x08 && data[5] == 0x3e)
                                        bytescache.Add(data);
                                    else
                                        COM.SendUartData(data);
                                    lastcmd = data[5];
                                    break;
                                case 0xf5:
                                    u.b = bytes;
                                    u.ui[0] = 0x00000002;
                                    udp.Send(bytes, 2, user);
                                    break;
                                case 0xf7:
                                    u.b = bytes;
                                    u.ui[0] = 0x000005d9;
                                    u.ui[1] = 0x00000804;
                                    udp.Send(bytes, 8, user);
                                    break;
                                default:

                                    break;
                            }
                        }
                        else
                        {
                            

                            if (data[1] != 0)
                            {
                                int len = u.us[1];
                                //if ((len & 3) != 0)
                                //{
                                //    len >>= 2;
                                //    len++;
                                //    len <<= 2;
                                //}
                                var ret = stlink.CMD(len, data, 4, data[0] == 0 ? 2 : data[0]);
                                
                                if (data[5] == 0x3e && ret[0] != 0x80)
                                {
                                    u.b = ret;
                                    sb.AppendLine($"0x{u.ui[0]:X8} 0x{u.ui[1]:X8} 0x{u.ui[2]:X8}");
                                    u.b = lastpacket;
                                    sb.AppendLine($"{defstr[lastpacket[5]]} 0x{ret[0]:X2} LEN:{lastpacket.Length}");
                                    sb.AppendLine($"0x{(u.ui[1] >> 16) | (uint)u.us[4] << 16:X8} 0x{u.us[5]:X4}");
                                }
                                if (len == 0)
                                {
                                    ret[1] = 0x00;
                                    ret[0] = 0x80;
                                    len = 1;
                                }
                                udp.Send(ret, len, user);
                                stlink.CMDDATA.ul[0] = 0;
                                stlink.CMDDATA.ul[1] = 0;
                            }
                            else
                            {
                                cmd3 = data[5];
                                stlink.CMD(0, data, 4, data[0]);

                                if (data[5] == 0x08)
                                {
                                    if (((u.ui[1] >> 16) | (uint)u.us[4] << 16) < 0x9000000)
                                    {
                                        sb.AppendLine($"WD 0x{(u.ui[1] >> 16) | (uint)u.us[4] << 16:X8} 0x{u.us[5]:X4}");
                                    }
                                }

                                for (int i = 0; i < u.us[1]; i++)
                                    bytes[i] = data[i + 4 + data[0]];
                                stlink.CMD(true, u.us[1], bytes);
                                data[0] = 0x80;
                                data[1] = 0x00;
                                //udp.Send(data, 2, user);
                                stlink.CMDDATA.ul[0] = 0;
                                stlink.CMDDATA.ul[1] = 0;
                            }
                            lastpacket = data;
                        }
                    }
                }
            }).Start();
            var t = new System.Windows.Forms.Timer();
            t.Interval = 250;
            t.Tick += (s,e) => {
                if (user.Address != IPAddress.Any)
                {
                    info.Text = "目标"+user.ToString();
                }
                sort.Clear();
                sort.AddRange(callCountDefs.ToArray());
                if (sortupdate)
                {
                    sortupdate = false;
                    sort.Sort((x, y) => { return x.Count == y.Count ? 0 : x.Count > y.Count ? -1 : 1; });
                }
                for (int i = 0; i < lablist.Count; i++)
                {
                    if (sort[i].Count == 0) break;
                    lablist[i].Text = "[" + sort[i].Count + "]" + defstr[sort[i].id];
                    lablist[i].Visible = true;
                }
                c1.Text = defstr[cmd1];
                c2.Text = defstr[cmd2];
                if(cmd3!=0)
                    c3.Text = defstr[cmd3];
                if (sb.Length != 0)
                {
                    textBox1.AppendText(sb.ToString());
                    sb.Clear();
                }
            };
            t.Start();
            var to = new System.Windows.Forms.Timer();
            to.Interval = 1;
            to.Tick += (s, e) => {
                if (COM.COM.IsOpen)
                {
                    if (COM.rxbuffer.Count != 0)
                    {
                        if (bytescache.Count != 0)
                        {
                            COM.SendUartData(bytescache[0]);
                            bytescache.RemoveAt(0);
                            COM.rxbuffer.Clear();
                        }
                        else if (++COM.rxtimeout > 2)
                        {

                            var v = COM.rxbuffer.ToArray();
                            COM.rxbuffer.Clear();
                            union u = new union();
                            u.b = v;
                            sb.AppendLine($"< 0x{u.ui[0]:X8} 0x{(u.b.Length>4? u.ui[1]:0):X8}");
                            if (user.Address != IPAddress.Any)
                                udp.Send(v, v.Length, user);
                        }
                    }
                }
            };
            to.Start();
            button1_Click(null, null);
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void retryopen_Click(object sender, EventArgs e)
        {
            var h = stlink.getHandle();
            info.Text = "等待连接...";
            if (h.Count != 0)
                retryopen.Visible = !stlink.openSTLink(h[0]);
            else
                info.Text = "打开stlink失败";
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            textBox1.Visible = !textBox1.Visible;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            var dt = new byte[4];
            union u = new union();
            if (2 != stlink.getMode())
            {
                stlink.exitDFU();
                stlink.getMode();
                stlink.SWDMode();
                stlink.getMode();
            }
            stlink.SWDSpeed(SWDCLK.STLINK_SWDCLK_100KHZ_DIVISOR);
            //stlink.readMem(0x20000000, 4, dt);
            //u.b = stlink.CMD(12, Cmd.DebugCommand, (Cmd)0x3e);
            stlink.Halt();
            stlink.ReadReg(0);
            var x= stlink.ReadDAP(0xfc);
            //stlink.WriteDAP(0x00, 0x23000052);
            //x=stlink.ReadDAP(0);
            //stlink.WriteDAP(0x00, 0x23000050);
            //stlink.WriteDAP(0x04, 0x20000001);
            //x=stlink.ReadDAP(0x0c);
            //x=stlink.ReadDAP(0x0c);
            //x = stlink.ReadDAP(0x0c);
            var xx = new byte[128];
            var xx2 = new byte[128];
            var xx3 = new byte[128];
            union xx4 = new union();
            xx4.b = xx;
            xx4.ui[0] = 0x12345678;
            xx4.ui[1] = 0x90ABCDEF;
            stlink.writeMem8(0x20000001, 16, xx);
            stlink.readMem8(0x20000001, 16, xx);
            stlink.readMem16(0x20000002, 16, xx2);
            stlink.writeMem16(0x20000002, 16, xx);
            stlink.readMem(0x20000004, 16, xx3);

        }

        private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            panel3.Visible = !panel3.Visible;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            foreach (var v in callCountDefs) v.Count = 0;
            sortupdate = true;
            for (int i = 0; i < lablist.Count; i++)
            {
                lablist[i].Text = "";
                lablist[i].Visible = false;
            }
        }
    }
}
